04_HTTP 协议

1到6节课,讲的都是概念,多看几遍看懂就行。从第7节开始讲解创建http服务,http模块是一个非常重要的模块,因为像express库、koa库等都是对http库的封装而已,自己完全可以使用http模块创建一个服务器。

1.概念

HTTP(hypertext transport protocol)协议;中文叫超文本传输协议

image-20230823221010161

image-20230823221021023

image-20230823221133940

image-20230823221157114

是一种基于TCP/IP的应用层通信协议,这个协议详细规定了 浏览器 和万维网 服务器 之间互相通信的规则。

协议中主要规定了两个方面的内容:

报文:可以简单理解为就是一堆字符串

2.请求报文的组成

看报文专业利器,先安装fiddle,老师的资料中有。

提示:

虽然在Chrome浏览器的F12中可以很清楚的看到请求报文、响应报文,但是有些情况下,还是需要外部的软件来查看,什么情况呢?比如说我之前做的支付项目,进入到一个界面之后,自动展示支付宝的formdata,而浏览器url一换之后,之前的请求就消除了,看不到了,只能看到支付宝的请求信息。

那么通过fiddle这类的工具,在这种情况下,就可以一直监听,得到想要的内容。

image-20230823221522226

fiddle软件直接安装即可,等不用了就删除掉。fiddle安装后不会产生桌面图标,在win里面找一下fiddle,打开。

弹窗不用管,直接点否。首先进行配置,tools→options→https,进行配置,配置完成后,重启fiddle。

image-20230823222051767

image-20230823222034308

image-20230823222140609

image-20230823222329722

image-20230823222507261

image-20230823222654587

image-20230823223005440

后面会具体说明这些内容。

3.HTTP 的请求行

image-20230823223053593

image-20230823223210824

image-20230823223241443

image-20230823223321217

image-20230823223414405

image-20230823223540584

image-20230823223717441

 

4.HTTP 请求头

image-20230823223757151

格式:『头名:头值』

常见的请求头有:

请求头解释
Host主机名
Connection连接的设置 keep-alive(保持连接);close(关闭连接)
Cache-Control缓存控制 max-age=0 (没有缓存)
Upgrade-Insecure-Requests将网页中的http请求转化为https请求(很少用),比如老网站升级
User-Agent用户代理,客户端字符串标识,服务器可以通过这个标识来识别这个请求来自哪个客户端,一般在PC端和手机端的区分
Accept设置浏览器接收的数据类型
Accept-Encoding设置接收的压缩方式
Accept-Language设置接收的语言 q=0.7 为喜好系数,满分为1
Cookie后面单独讲

上面的只是常见的请求头,能记多少是多少,在创建服务器时会用到这些设置,边做边记。具体的请求头含义可以参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

5.HTTP 的请求体

image-20230823224259262

请求体内容的格式是非常灵活的,

(可以是空)==> GET请求,

(也可以是字符串,还可以是JSON)===> POST请求

例如:

6.响应报文的组成·

image-20230824220642855

image-20230824220806853

image-20230824220856527

image-20230824220950332

image-20230824221037117

image-20230824221203636

image-20230824221412742

image-20230824221834740

image-20230824221857005

image-20230824221944469

 

HTTP/1.1:HTTP协议版本号

200:响应状态码 404 Not Found 500 Internal Server Error。还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

OK:响应状态描述

响应状态码和响应字符串关系是一一对应的。

不需要把每一个都记住,记住常用的就行了。参考:https://developer.mozilla.org/zh-CN/docs/Glossary/Response_header

响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON

网络基础概念

IP

image-20230824222218916

image-20230824222430657

image-20230824222513077

image-20230824222548653

image-20230824222625020

image-20230824222735428

image-20230824223006692

image-20230824223039514

image-20230824223102471

image-20230824223229121

image-20230824223359062

IP的分类

image-20230824223704567

image-20230824223832040

image-20230824224513308

image-20230824224625878

image-20230824224832753

image-20230824224721973

image-20230824225007614

image-20230824225121089

image-20230824225138168

image-20230824225245110

端口

image-20230824225441415

image-20230824225502549

image-20230824225734331

image-20230824230039703

image-20230824230102644

7.创建 HTTP 服务

使用 nodejs 创建 HTTP 服务

7.1 操作步骤

http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

7.2 测试

浏览器请求对应端口

image-20230902162011398

7.3 注意事项

  1. 命令行ctrl + c,可以停止服务
  2. 当服务启动后,更新代码之后, 必须重启服务才能生效
  3. 响应内容中文乱码的解决办法

当响应内容有中文时,会产生乱码:

image-20230902163214697

需要设置响应头:

 

image-20230902163401048

 

  1. 端口号被占用

解决办法:

1)关闭当前正在运行监听端口的服务 ( 使用较多,一般是自己在调试的时候不小心多次启动服务器了,所以可以这样做。真正的生产环境肯定是不能这么做的,要被别人叼。 )

2)修改其他端口号

  1. HTTP 协议默认端口是 80 。HTTPS 协议的默认端口是 443, HTTP 服务开发常用端口有 3000,8080,8090,9000 等

如果端口被其他程序占用,可以使用 资源监视器(按win→windows管理工具→资源监视器) 找到占用端口的程序的PID,然后使用 任务管理器 关闭对应的程序。

image-20230902163831761

image-20230902164007698

8.浏览器查看 HTTP 报文

点击步骤

image-20230413141603915

8.1 查看请求行与请求头

image-20230413143704019

8.2 查看请求体

image-20230413143730821

8.3 查看 URL 查询字符串

image-20230413143748263

8.4 查看响应行与响应头

image-20230413143803365

8.5 查看响应体

image-20230413143816215

9.获取 HTTP 请求报文

获取请求报文和设置响应报文,老师都是用一个一个例子讲解的,并不难,但是很琐碎,不好记笔记。觉得重要的我会记下来,但是复习的时候,还是一个个的输出看一下。在请求时可以直接在url后面拼接query参数,来模拟。

主要还是在http.createServer({})里面输出不同的语法规则来学习。

想要获取请求的数据,需要通过 request 对象。

含义语法重点掌握
请求方法request.method*
请求http版本request.httpVersion 
请求路径request.url*
URL 路径const url = require("url")
url.parse(request.url).pathname
*
URL 查询字符串const url = require("url")
url.parse(request.url,true).query
*
请求头request.headers*
请求体request.on('data',function(chunk){})
request.on('end',function(){})
 

因为get请求的请求体一般是空的,所以需要使用form表单来搭建一个post请求。(但是在复习的时候就没有必要这么做了,因为比较麻烦,所以还是用apifox来发起请求方便些。)

用open with live server打开,发起请求,这样我们设置的服务器中就可以看到请求体了。

注意事项:

  1. request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容

其实看到这句话,我还是有点懵的,路径以及查询字符串是什么?看一下前面的内容就知道了。

image-20230902193340093

  1. request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
  2. 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『/
  3. 关于 favicon.ico:这个请求是属于浏览器自动发送的请求
  4. 解析url路径和字符串,node@18之后不建议使用url.parse,而是使用URL类。

浏览器输入http://127.0.0.1:9000/search?keyword=123看一下效果。

image-20231109150240806

我只请求了一次啊,为什么会返回两个URL呢?因为下面这个favicon.ico是无论哪个网站都会默认请求的,不需要管。

image-20230902201228746

9.1 练习

按照以下要求搭建 HTTP 服务

请求类型(方法)请求地址响应体结果
get/login登录页面
get/reg注册页面

我自己写的答案:

小节:

其实学到这里,我就感觉很高兴了,因为我终于弄清楚前后端分离应该怎么做了,部分原理总算弄明白了,也知道前后端不分离可以怎么做了。就是利用URL的不同来达到不同的目的,前端部分用一个url,后端部分用一个url,即使前后端部署到同一个主机上,那么也可以利用端口号的不同,来实现通信。这样即使前端url和后端url的路径有重名情况,也不会有任何影响,真的是感叹人类智慧的无限。

另外:

在调试后端url的时候,我们是直接在浏览器的地址栏中输入的url,返回的结果会直接显示在浏览器界面中,但其实我一直以来用的前后端分离技术,是前端先调用后端url,获取数据,然后将处理好的数据渲染到界面上。那么直接在浏览器中输入后端url并返回后端数据,有没有作用呢?应该有作用,这就是服务端渲染的技术,等我学nextjs的时候,可以看到,现在先不管。到时候可以看一下浏览器输入的是前端url还是直接输入后端url,我感觉应该还是前端url,因为前后端的端口号不同,别人在使用的时候,不一定会特意记住端口号的不同。(不应该这么说:“在浏览器里面输入后端url”,不应该是这样的,只不过是在我学习的时候是在浏览器中输入后端url。真正的项目还是应该前端来请求接口,只不过后端返回的直接是一个html页面,可以直接渲染到页面上,避免了前端的一些处理会更快一些。)

10.设置 HTTP 响应报文

注意老师的用词,对HTTP请求报文,是“获取”,对HTTP响应报文,是“设置”,意味着不同的操作。

作用语法
设置响应状态码response.statusCode
设置响应状态描述response.statusMessage (用的非常少)
设置响应头信息response.setHeader('头名','头值')
设置响应体response.write('xx')
response.end('xx')

image-20230905114739974

image-20230905114617280

10.1 练习

搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示

分析:题目需求其实是,服务端返回的是一个HTML文件的内容,里面有表格,并且里面可以编写css和js来实现样式和功能,那么可以使用模板字符串来编写html代码。可能为单元格绑定点击事件有点难度,因为一直都没有使用dom操作来绑定事件,搜索一下就知道了。

优化:

上面的HTML代码是在js文件里面编写的,没有语法高亮、没有代码提示,编写起来非常不方便,有没有办法能够解决这个痛点呢?

可以将html代码单独放到一个html文件里面来编写,使用fs模块来读取这个HTML文件,并作为返回结果返回。

 

这样做还有一个好处,就是更改了html文件里面的代码,不用重启服务器,当重新发送请求的时候,就会返回更改的内容。为什么呢?因为发起请求的时候,fs会重新读取HTML里面的内容,那么返回的就是最新的内容了。

11.网页资源的基本加载过程

image-20230413150745740

image-20230413150802608

网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML ,再发送其他资源的请求(这就是为什么css、js、图片等静态资源都放在html标签里面进行获取),如 CSS,Javascript,图片 等(这些请求的发送是并行的,并没有绝对的先后之分)。 理解了这个内容对于后续的学习与成长有非常大的帮助

11.1 扩展练习:

在10.1练习中,HTML文件里面写了CSS和JS,现在要求将css和js代码全部提取到单独的文件中,在HTML文件中引入这些文件,达到同样的效果,怎么做?

分析:将css用link标签导入,js用script标签导入,看效果:

服务器代码不需要变化,引入的还是html文件,实际效果是:

image-20230905143217021

这一看就是css代码没有导入,推测js代码也没有导入。但是文件是请求并返回了的啊:

image-20230905143342470

但是仔细看一下index.css和index.js返回的内容:

image-20230905143644617

image-20230905143705304

可以看到css、js文件的响应结果都是html的代码,为什么?因为请求url虽然不同:

image-20230905143838084

image-20230905143907647

image-20230905143922581

但是走的都是这段代码。

上述代码并没有对request请求做处理,所以返回的都是html。可以根据request.url的pathname判断,返回不同的结果。

再访问OK。

这个案例看上去很简单,但其实很重要,它已经做到了前端开发服务器的基本功能了,而且可以做到热更新,我如果编写html文件,完全可以用这个服务器来查看实际效果,这实际上就达到了vscode中的open with live server这个插件的基本功能了。

这是一个重要的开始,不管是vue/cli、create-react-app还是vite,这些脚手架都是从这个简单的服务器开始的。

我又想到了,平时最常使用的npm run dev和npm run build命令,就是重点依靠fs模块,读取和写入操作,真的是有点熟悉的感觉了。

12.静态资源服务

静态资源是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等。

动态资源是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等。

练习:搭建静态资源服务,需求如下:

image-20230905213317733

分析:这个需求并不是将css、image都放到一个html文件里面请求(也就是说不用在html文件里面引入css或者写img标签),而是直接在浏览器地址栏中使用http://127.0.0.1:9000/index.htmlhttp://127.0.0.1:9000/css/app.css等这种url的方式直接请求,能够得到文件内容即可。其实这种效果我们之前已经实现过了。

image-20230905214027493

但是里面有太多的if...else if的判断条件,如果我要添加文件,就要添加判断条件,有没有办法去掉这么多判断条件呢?可以,因为fs读取的文件路径和pathname是有关联的,统一构造文件地址即可。

 

这个练习其实生动的展示了tomcat服务器开启服务的过程。项目打包的文件放到一个文件夹里面,一般是我使用npm run build生成的dist文件夹里面的文件,内容是这些:

image-20231110092330820

放到tomcat的webapps里面。

image-20230906092254605

访问就输入http://172.21.21.64:8080/info-collector来访问。tomcat会帮助返回文件夹里面的内容。

这个过程现在完全可以用nodejs来实现了,一定要找时间自己实现一下


其实这里要搞清楚一点,在上面的案例中,其实是根据路径pathname的不同,返回不同的文件的。但是在tomcat里面,路径都是一样的,那该怎么返回文件呢?

这一点其实很好解释,我使用的路径是/info-collector,这个路径名返回的是index.html文件,其余的文件都在html文件里面引入了,所以在html文件里面引入别的文件的时候,强烈建议写绝对路径,而vue生成的html文件里面就是这样做的。

image-20230906094344398

那么服务端获取到的文件pathname就自动带上了/info-collector,我也不需要进行特殊处理了。直接读取即可。

不会有任何影响,只要服务端代码能够根据地址找到正确的文件即可。

image-20230906093437627

image-20230902193340093


在学习vue的时候,我们知道有hash模式和history模式,在发布项目之后,我总是给别人的访问url上带上了/#/,比如说:http://172.21.21.64:8080/info-collector/#/,其实#/不需要加上,因为这部分内容是vue帮我们生成的,我直接在地址栏里面输入``,返回的是:

image-20230906094028102

image-20230906094127717

而这个html文件中,引入了css、js、图片等文件:

image-20230906094344398

所以服务器会请求这些文件,就像上面的案例展示的一样。

瞬间感觉vue的一部分原理我搞清楚了。嘿嘿。

自己用nodejs创建服务器,部署实际项目。

image-20230906105721141

输入http://127.0.0.1:9000/dist/进行访问,效果非常OK:

尝试将项目包文件夹名改为info-collector,相应的代码改一下,访问也是OK的。

上面的是前端项目的部署,其实后端项目的部署也是一样的,也是部署打包之后的文件,只需要找到主文件即可。真的是学到了一个重要的知识点。

12.1 网站根目录或静态资源目录

HTTP 服务在哪个文件夹中寻找静态资源,哪个文件夹就是 静态资源目录 ,也称之为 网站根目录

这一点必须理解清楚,因为写server的时候,可能要用到比较灵活的网站根目录,上面案例中,在读取文件的时候,可以定义一个变量let root = __dirname + "/pages",作为网站的根目录,读取文件时拼接上pathname就行了。那么当根目录需要修改的时候,只需要修改root变量即可。

思考:vscode 中使用 live-server 访问 HTML 时, 它启动的服务中网站根目录是谁?

答案:是vscode打开的文件夹。

做一个实验验证一下,使用live-server打开test/a/b/c/index.html,看一下浏览器地址栏的url。

image-20230905215339254

image-20230905215258693

可以看到,网站的根目录确实是vscode打开的文件夹,在这里就是test文件夹,地址标记就是a前面那个/

12.2 网页中的 URL

网页中的 URL 主要分为两大类:相对路径与绝对路径。

注意:这里说的是网页中的URL,指的就是html文件中的url,具体就是html标签里面使用的url。

12.2.1 绝对路径

绝对路径可靠性强,而且相对容易理解,在项目中运用较多。

形式特点备注
http://atguigu.com/web直接向目标资源发送请求,容易理解。网站的外链会用到此形式。完整url
//atguigu.com/web与当前页面 URL 的协议拼接形成完整 URL 再发送请求。大型网站用的比较多。url省略了http协议
/web与当前页面 URL 的协议、主机名、端口拼接形成完整 URL 再发送请求。中小型网站用的比较多。url省略了http协议、域名、端口号

示例:

效果:

12.2.2 相对路径

相对路径在发送请求时,需要与当前页面 URL 路径进行 计算 ,得到完整 URL 后,再发送请求,学习阶段用的较多。

示例:假如当前网页 url 为 http://www.atguigu.com/course/h5.html,当前文件夹就是course文件夹,下面的相对路径形式的最终的url就得到这些结果:

形式最终的URL备注
./css/app.csshttp://www.atguigu.com/course/css/app.css表示当前文件夹下的资源
js/app.jshttp://www.atguigu.com/course/js/app.js表示当前文件夹下的资源
../img/logo.pnghttp://www.atguigu.com/img/logo.png表示当前文件夹同一层级的文件夹img下的logo.png
../../mp4/show.mp4http://www.atguigu.com/mp4/show.mp4因为 ../ 已经到了最顶层了,所以2个 ../ 效果只是一个 ../的效果

到底应该使用相对路径还是绝对路径,需要总结一下,上面说相对路径在学习阶段用的比较多,注意这句话的前提是:网页里面的URL,也可以理解为html文件里面的url。

但是我目前做前后端分离开发,界面都是使用UI库来开发,vue/react等,在里面应该使用那种路径呢?

在vue组件里面,引入组件、标签里面引入文件;里面的js文件,引入别的js、css等文件,都可以使用相对路径,也可以使用@/开头,表示src文件夹。也可以使用绝对路径,但是这个绝对路径的根目录就是public文件夹了,除非你引入的文件存放在public文件夹里面,否则不要使用绝对路径。

在react组件里面,引入组件、js文件、css文件等,都是使用相对路径。前端开发使用相对路径没有问题,这是因为vue/react框架会为我们处理这些相对路径,打包之后的文件中,都会使用绝对路径。

vue/react框架里面有html文件,在这里面推荐使用绝对路径,因为里面的一些资源都放在了公共文件夹里面了,到时候试一下就知道了。

12.2.3 网页中使用 URL 的场景小结

包括但不限于如下场景:

12.3 设置资源类型(mime类型)

媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。

HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源。

下面是常见文件对应的 mime 类型:

对于未知的资源类型,可以选择 application/octet-stream 类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

练习:对12.静态资源服务的练习,对不同的文件设置资源类型。

需求:解决乱码问题。在js、css文件中,如果有中文,是会出现乱码的,之前我们解决乱码问题的方式是response.setHeader("content-type","text/html;charset=utf-8"),但这里是js、css等文件,很明显不能设置为text/html,需要单独设置,其实也很简单,在得到具体的type后面拼接上;charset=utf-8就行了。

疑问:当没有设置html的乱码兼容时,HTML文件里面的中文并不会是乱码,这是为什么呢?

因为HTML文件里面有一句代码<meta charset="UTF-8">,已经设置了字符。那如果我在服务器代码里面设置了HTML的响应头呢,以哪个为主呢?

可以自己改一下代码,答案是设置的html响应头优先级更高一些。

小节:其实网站的css、js等文件的响应头没有必要加上;charset=utf-8,因为这些文件虽然返回的时候中文乱码了,但是这些文件加载到html中之后,会按照html的字符集来解析,而一般html的字符集都设置为了utf-8,所以不会在网页中出现乱码。


完善错误处理:

还是上面的案例,在fs读取文件的时候,可能有多种错误类型,要根据不同的错误类型返回不同的信息。

image-20230906220844555

12.4 GET 和 POST 请求场景小结

GET 请求的情况:

POST 请求的情况:

13.GET和POST请求的区别

GET 和 POST 是 HTTP 协议请求的两种方式。主要有如下几个区别:

05_Node.js 模块化

1.介绍

1.1 什么是模块化与模块 ?

将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化

其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。

1.2 什么是模块化项目 ?

编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目。

1.3 模块化好处

下面是模块化的一些好处:

  1. 防止命名冲突
  2. 高复用性
  3. 高维护性

2.模块暴露数据

2.1 模块初体验

可以通过下面的操作步骤,快速体验模块化

  1. 创建 me.js
  1. 创建 index.js

执行node index.js,查看效果:

image-20230911112348975

2.2 暴露数据

模块暴露数据的方式有两种:

  1. module.exports = value
  2. exports.name = value

使用时有几点注意:

exports = module.exports = {} ,require 返回的是目标模块中 module.exports 的值。

image-20230413153044047

image-20230909203138540

3.导入(引入)模块

在模块中使用 require 传入文件路径即可引入文件。

require 使用的一些注意事项:

  1. 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./../。(在讲解fs模块的时候,因为相对路径会受到工作目录的影响,老师是强烈建议使用绝对路径的,这里为什么推荐使用相对路径呢?因为在使用require引入时,不会受到工作目录的影响,并且相对路径写起来比较简单。)

  2. 在导入jsjson 文件时,可以不用写后缀(如果导入的js文件和json文件同名,在导入时都省略了后缀,那导入的是哪个文件呢?导入的是js文件。),导入c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到。

  3. 如果导入其他类型的文件,会以 js 文件进行处理。

  4. 如果导入的路径是个文件夹(就是经常使用的导入第三方模块的用法),则会 首先 检测该文件夹下 package.json 文件中 main 属性对应的文件,如果存在则导入,如果文件不存在会报错。

    如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.jsindex.json ,如果没找到,就会报错。

    案例:

    image-20230909204912728

    运行node index.js

    image-20230909205211536

     

  5. 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./../

4.导入模块的基本流程

这里我们介绍一下 require 导入 自定义模块 的基本流程

  1. 将相对路径转为绝对路径,定位目标文件
  2. 缓存检测
  3. 读取目标文件代码
  4. 包裹为一个函数并执行(自执行函数)。(通过 arguments.callee.toString() 查看自执行函数)
  5. 缓存模块的值
  6. 返回 module.exports 的值

image-20230413153351433

导入模块的伪代码:

感想:

如何将已经学过的东西,组合起来写成工具解决问题,这个我几乎没有经验、也没有这种想法(我说的是工具,不是项目),今天学习Nodejs的require的基本流程,伪代码基本写完了流程,是按照上面的中文流程写出来的,我能够做到吗?可能刚开始很难,但要慢慢的去做。

5.CommonJS 规范

module.exportsexports 以及 require 这些都是 CommonJS 模块化规范中的内容。

而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript。